﻿using System;
using System.Collections;
using System.Collections.Generic;
using Adaptive.Foundation.Tokenizers;

namespace Adaptive.Foundation.Parsing
{
    /// <summary>
    /// Provides a base implementation for defining clases which perform specific parsing
    /// tasks using the rule structure.
    /// </summary>
    /// <remarks>
    /// 
    /// </remarks>
    public abstract class ParserBase<T> : AiObjectBase, IParser<T>
    {
        /*--------------------------------------------------------------------------------
         * Public Events
         *------------------------------------------------------------------------------*/
        #region Public Events
        /// <summary>
        /// Occurs when an error is encountered during processing.
        /// </summary>
        public event ExceptionEventHandler ParsingException;
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// List of rules.
        /// </summary>
        private ParsingTokenRuleCollection _rules;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        protected ParserBase()
            : base()
        {
            _rules = new ParsingTokenRuleCollection();
            InitializeRules();
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                if (_rules != null)
                    _rules.Dispose();
            }

            _rules = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets the reference to the list of rules used in the parsing process.
        /// </summary>
        /// <value>
        /// A <see cref="ParsingTokenRuleCollection"/> containing the list of rules.
        /// </value>
        public virtual ParsingTokenRuleCollection Rules
        {
            get { return _rules; }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods
        /// <summary>
        /// Raises the <see cref="E:ParsingException"/> event.
        /// </summary>
        /// <param name="e">The <see cref="Anderson.Foundation.ExceptionEventArgs"/> instance containing the event data.</param>
        protected virtual void OnParsingException(ExceptionEventArgs e)
        {
            if (ParsingException != null)
                ParsingException(this, e);
        }
        /// <summary>
        /// Applies the supplied rule to the supplied token.
        /// </summary>
        /// <param name="resultContainer">
        /// The object instance containing and managing the parsing process.
        /// </param>
        /// <param name="sourceList">
        /// The original list of tokens being processed.
        /// </param>
        /// <param name="currentIndex">
        /// The current position in <i>sourceList</i> for the token being processed.
        /// </param>
        /// <param name="token">
        /// The <see cref="RawToken"/> value to be processed.
        /// </param>
        /// <param name="rule">
        /// The <see cref="ParsingTokenRule"/> to apply to the current token and, if valid,
        /// subsequent data.
        /// </param>
        /// <returns>
        /// The index of the next token to process by the calling method.
        /// </returns>
        protected abstract int ApplyRuleForToken(T resultContainer, RawTokenCollection sourceList, int currentIndex,
            RawToken token, ParsingTokenRule rule);
        /// <summary>
        /// Applies the current parsing token as generic data.
        /// </summary>
        /// <param name="resultContainer">
        /// The object instance containing and managing the parsing process.
        /// </param>
        /// <param name="sourceList">
        /// The original list of tokens being processed.
        /// </param>
        /// <param name="currentIndex">
        /// The current position in <i>sourceList</i> for the token being processed.
        /// </param>
        /// <param name="token">
        /// The <see cref="RawToken"/> value to be processed.
        /// </param>
        /// <returns>
        /// The index of the next token to process by the calling method.
        /// </returns>
        protected abstract int ApplyTokenAsData(T resultContainer, RawTokenCollection sourceList, int currentIndex,
            RawToken token);
        /// <summary>
        /// Applies the supplied rule to the supplied token.
        /// </summary>
        /// <param name="resultContainer">
        /// The object instance containing and managing the parsing process.
        /// </param>
        /// <param name="sourceList">
        /// The original list of tokens being processed.
        /// </param>
        /// <param name="currentIndex">
        /// The current position in <i>sourceList</i> for the token being processed.
        /// </param>
        /// <param name="list">
        /// The <see cref="RawTokenCollection"/> list of tokens to be processed by rule.
        /// </param>
        /// <param name="rule">
        /// The <see cref="ParsingTokenRule"/> to apply to the current token and, if valid,
        /// subsequent data.
        /// </param>
        /// <returns>
        /// The index of the next token to process by the calling method.
        /// </returns>
        protected abstract int ApplyPatternRuleForToken(T resultContainer, RawTokenCollection sourceList, int currentIndex,
            RawTokenCollection list, ParsingTokenRule rule);
        /// <summary>
        /// Initializes the parsing rule instances.
        /// </summary>
        protected abstract void InitializeRules();
        /// <summary>
        /// Creates a new object instance.
        /// </summary>
        /// <returns>
        /// A new oject instance.
        /// </returns>
        protected abstract T InstantiateNewItem();
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Parses the supplied token list into the return object.
        /// </summary>
        /// <param name="tokenList">
        /// A list of tokens to be processed.
        /// </param>
        /// <returns>
        /// An object instance rendered from the token data.
        /// </returns>
        public virtual T Parse(RawTokenCollection tokenList)
        {
            RawTokenCollection patternList = null;  //Local container for pattern tokens.
            RawToken tokenPtr = null;               //Pointer to the current token being processed.
            T returnValue = default(T);             //Return value.
            int itemIndex = 0;                      //Current token item index.
            int length = 0;                         //Iteration limit.

            returnValue = InstantiateNewItem();
            length = tokenList.Count;
            patternList = new RawTokenCollection();

            if (length > 0)
            {
                do
                {
                    //Get the token to process.
                    tokenPtr = tokenList[itemIndex];

                    //Apply list rules first.
                    if (patternList.Count == 0)
                    {
                        //If the pattern is empty...
                        //
                        if (_rules.IsTokenInRulePatternStart(tokenPtr))
                        {
                            //Start new pattern.
                            patternList.Add(tokenPtr);
                        }
                        else if (_rules.IsRuleForToken(tokenPtr))
                        {
                            //Apply singleton rule.
                            itemIndex = ApplyRuleForToken(returnValue, tokenList, itemIndex, tokenPtr,
                                _rules.GetRuleForToken(tokenPtr));
                        }
                        else
                        {
                            //Assume the token represents generic data.
                            itemIndex = ApplyTokenAsData(returnValue, tokenList, itemIndex, tokenPtr);
                        }
                    }
                    else
                    {
                        //If a pattern is underway...
                        //
                        patternList.Add(tokenPtr);

                        //1. Determine if the pattern is done... 
                        //2. Determine if the pattern continues...
                        //3. If the pattern no longer matches, process each element individually amd 
                        //   eliminate the list.
                        if (_rules.PatternMatch(patternList))
                        {
                            //End and process pattern.
                            itemIndex = ApplyPatternRuleForToken(returnValue, tokenList, itemIndex,
                                patternList, _rules.GetPatternRuleForList(patternList));
                            patternList.Clear();
                        }
                        else if (_rules.IsInPattern(patternList))
                        {
                            //Continue pattern
                        }
                        else
                        {
                            //No pattern match; clear list, process individually.
                            foreach (RawToken subtoken in patternList)
                            {
                                if (_rules.IsRuleForToken(subtoken))
                                    itemIndex = ApplyRuleForToken(returnValue, tokenList, itemIndex,
                                        subtoken, _rules.GetRuleForToken(subtoken));
                                else
                                    itemIndex = ApplyTokenAsData(returnValue, tokenList, itemIndex,
                                        subtoken);
                            }
                            patternList.Clear();
                        }

                    }
                    itemIndex++;
                } while (itemIndex < length);
            }
            return returnValue;
        }
        /// <summary>
        /// Parses the lists of tokens into thier appropriate object instances.
        /// </summary>
        /// <param name="listOfTokenLists">
        /// A list of lists of tokens to be processed.
        /// </param>
        /// <returns>
        /// A list of object instances rendered from the token data.
        /// </returns>
        public virtual List<T> Parse(List<RawTokenCollection> listOfTokenLists)
        {
            List<T> returnValue = null;                 //Return value.

            returnValue = new List<T>();
            foreach (RawTokenCollection itemList in listOfTokenLists)
            {
                returnValue.Add(Parse(itemList));
            }
            return returnValue;
        }
        #endregion
    }
}